package common

import (
	"context"
	"log"
	"strings"
	"sync"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

type ServiceChannel struct {
	sync.Mutex
	serviceName string
	index       int
	conns       []*grpc.ClientConn
	hosts       map[string]*grpc.ClientConn
}

func NewServiceChannel(name string) *ServiceChannel {
	return &ServiceChannel{
		serviceName: name,
		conns:       make([]*grpc.ClientConn, 0),
		hosts:       make(map[string]*grpc.ClientConn),
	}
}

func (s *ServiceChannel) append(host string) {
	opts := []grpc.DialOption{
		grpc.WithBlock(),
		grpc.WithReturnConnectionError(),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	conn, err := grpc.DialContext(ctx, host, opts...)
	if err != nil {
		log.Printf("初始化 %s-%s 连接失败: %v(超时:%vs)", s.serviceName, host, err, 5)
		return
	}

	s.Lock()
	defer s.Unlock()

	s.conns = append(s.conns, conn)
	s.hosts[host] = conn
}

func (s *ServiceChannel) remove(host string) {
	s.Lock()
	defer s.Unlock()

	conn, exists := s.hosts[host]
	if !exists {
		log.Printf("%s-%s 节点删除连接时未找到记录", s.serviceName, host)
		return
	}

	if err := conn.Close(); err != nil {
		log.Printf("%s-%s 关闭连接失败: %v", s.serviceName, host, err)
	}

	for i, c := range s.conns {
		if c == conn {
			s.conns = append(s.conns[:i], s.conns[i+1:]...)
			break
		}
	}
	delete(s.hosts, host)
}

func (s *ServiceChannel) choose() *grpc.ClientConn {
	s.Lock()
	defer s.Unlock()

	if len(s.conns) == 0 {
		return nil
	}

	s.index = (s.index + 1) % len(s.conns)
	conn := s.conns[s.index]

	return conn
}

type ServiceManager struct {
	sync.Mutex
	concern  map[string]struct{}
	services map[string]*ServiceChannel
}

func NewServiceManager() *ServiceManager {
	return &ServiceManager{
		concern:  make(map[string]struct{}),
		services: make(map[string]*ServiceChannel),
	}
}

func (sm *ServiceManager) Choose(service string) *grpc.ClientConn {
	sm.Lock()
	sc, exists := sm.services[service]
	sm.Unlock()

	if !exists {
		log.Printf("当前没有提供 %s 的节点", service)
		return nil
	}

	return sc.choose()
}

func (sm *ServiceManager) AServiceOnline(serviceInstance, host string) {
	serviceName := GetServiceName(serviceInstance)
	sm.Lock()
	if _, isConcern := sm.concern[serviceName]; !isConcern {
		sm.Unlock()
		log.Printf("{%s}-{%s}上线但并不关心", serviceName, host)
		return
	}

	var service *ServiceChannel
	if sc, exists := sm.services[serviceName]; exists {
		service = sc
	} else {
		service = NewServiceChannel(serviceName)
		sm.services[serviceName] = service
	}
	sm.Unlock()

	service.append(host)
	log.Printf("{%s}-{%s} 服务节点添加管理", serviceName, host)
}

func (sm *ServiceManager) AServiceOffline(serviceInstance, host string) {
	serviceName := GetServiceName(serviceInstance)
	sm.Lock()
	if _, isConcern := sm.concern[serviceName]; !isConcern {
		sm.Unlock()
		return
	}
	sc, exists := sm.services[serviceName]
	sm.Unlock()

	if !exists {
		log.Printf("未找到服务 %s 的管理对象", serviceName)
		return
	}

	sc.remove(host)
	log.Printf("{%s}-{%s} 服务节点注销管理", serviceName, host)
}

func (sm *ServiceManager) Declare(serviceName string) {
	sm.Lock()
	sm.concern[serviceName] = struct{}{}
	sm.Unlock()
	log.Printf("声明关注服务: %s", serviceName)
}

func GetServiceName(instance string) string {
	pos := strings.LastIndex(instance, "/")
	if pos == -1 {
		return instance
	}
	return instance[:pos]
}
